package com.smile.commons.gps;

import com.smile.commons.gps.domain.CoordinatePointReq;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

/**
 * 面积计算工具类
 * 用于计算多边形覆盖面积
 * 
 * @author System
 * @since 2025-10-14
 */
public class AreaCalculationUtil {

    /**
     * 地球半径（米）
     */
    private static final double EARTH_RADIUS = 6378137.0;

    /**
     * 计算多边形覆盖面积（平方米）
     * 使用球面几何算法计算实际地球表面的多边形面积
     * 
     * @param coordinates 多边形顶点坐标列表
     * @return 面积（平方米）
     */
    public static double calculatePolygonArea(List<CoordinatePointReq> coordinates) {
        if (coordinates == null || coordinates.size() < 3) {
            return 0.0;
        }

        // 使用球面多边形面积计算方法
        double area = 0.0;
        int n = coordinates.size();

        for (int i = 0; i < n; i++) {
            CoordinatePointReq p1 = coordinates.get(i);
            CoordinatePointReq p2 = coordinates.get((i + 1) % n);

            if (p1 == null || p2 == null) {
                continue;
            }

            double lat1 = Math.toRadians(p1.getLatitude().doubleValue());
            double lon1 = Math.toRadians(p1.getLongitude().doubleValue());
            double lat2 = Math.toRadians(p2.getLatitude().doubleValue());
            double lon2 = Math.toRadians(p2.getLongitude().doubleValue());

            area += (lon2 - lon1) * (2 + Math.sin(lat1) + Math.sin(lat2));
        }

        area = Math.abs(area * EARTH_RADIUS * EARTH_RADIUS / 2.0);

        return area;
    }

    /**
     * 计算多边形覆盖面积（平方米）- 使用Shoelace公式（适用于小范围）
     * 
     * @param coordinates 多边形顶点坐标列表
     * @return 面积（平方米）
     */
    public static double calculatePolygonAreaShoelace(List<CoordinatePointReq> coordinates) {
        if (coordinates == null || coordinates.size() < 3) {
            return 0.0;
        }

        // 将经纬度转换为平面坐标（米）
        CoordinatePointReq reference = coordinates.get(0);
        double refLat = reference.getLatitude().doubleValue();
        double refLon = reference.getLongitude().doubleValue();

        double area = 0.0;
        int n = coordinates.size();

        for (int i = 0; i < n; i++) {
            CoordinatePointReq p1 = coordinates.get(i);
            CoordinatePointReq p2 = coordinates.get((i + 1) % n);

            if (p1 == null || p2 == null) {
                continue;
            }

            // 转换为平面坐标（米）
            double x1 = longitudeToMeters(p1.getLongitude().doubleValue(), refLon, refLat);
            double y1 = latitudeToMeters(p1.getLatitude().doubleValue(), refLat);
            double x2 = longitudeToMeters(p2.getLongitude().doubleValue(), refLon, refLat);
            double y2 = latitudeToMeters(p2.getLatitude().doubleValue(), refLat);

            area += x1 * y2 - x2 * y1;
        }

        return Math.abs(area / 2.0);
    }

    /**
     * 计算多边形覆盖面积并格式化输出
     * 
     * @param coordinates 多边形顶点坐标列表
     * @return AreaResult 面积结果对象
     */
    public static AreaResult calculatePolygonAreaWithFormat(List<CoordinatePointReq> coordinates) {
        double areaInSquareMeters = calculatePolygonArea(coordinates);
        return new AreaResult(areaInSquareMeters);
    }

    /**
     * 计算多边形周长（米）
     * 
     * @param coordinates 多边形顶点坐标列表
     * @return 周长（米）
     */
    public static double calculatePolygonPerimeter(List<CoordinatePointReq> coordinates) {
        if (coordinates == null || coordinates.size() < 2) {
            return 0.0;
        }

        double perimeter = 0.0;
        int n = coordinates.size();

        for (int i = 0; i < n; i++) {
            CoordinatePointReq p1 = coordinates.get(i);
            CoordinatePointReq p2 = coordinates.get((i + 1) % n);

            if (p1 == null || p2 == null) {
                continue;
            }

            perimeter += calculateDistance(p1, p2);
        }

        return perimeter;
    }

    /**
     * 计算两点间距离（米）
     * 
     * @param p1 点1
     * @param p2 点2
     * @return 距离（米）
     */
    private static double calculateDistance(CoordinatePointReq p1, CoordinatePointReq p2) {
        if (p1 == null || p2 == null) {
            return 0.0;
        }

        double lat1 = Math.toRadians(p1.getLatitude().doubleValue());
        double lon1 = Math.toRadians(p1.getLongitude().doubleValue());
        double lat2 = Math.toRadians(p2.getLatitude().doubleValue());
        double lon2 = Math.toRadians(p2.getLongitude().doubleValue());

        double dLat = lat2 - lat1;
        double dLon = lon2 - lon1;

        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                   Math.cos(lat1) * Math.cos(lat2) *
                   Math.sin(dLon / 2) * Math.sin(dLon / 2);

        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        return EARTH_RADIUS * c;
    }

    /**
     * 经度差转换为米
     */
    private static double longitudeToMeters(double lon, double refLon, double refLat) {
        double dLon = Math.toRadians(lon - refLon);
        return EARTH_RADIUS * dLon * Math.cos(Math.toRadians(refLat));
    }

    /**
     * 纬度差转换为米
     */
    private static double latitudeToMeters(double lat, double refLat) {
        double dLat = Math.toRadians(lat - refLat);
        return EARTH_RADIUS * dLat;
    }

    /**
     * 面积结果类
     */
    public static class AreaResult {
        /**
         * 面积（平方米）
         */
        private final double squareMeters;

        /**
         * 面积（亩）
         */
        private final double mu;

        /**
         * 面积（公顷）
         */
        private final double hectares;

        /**
         * 面积（平方千米）
         */
        private final double squareKilometers;

        public AreaResult(double squareMeters) {
            this.squareMeters = squareMeters;
            this.mu = squareMeters / 666.67;  // 1亩 = 666.67平方米
            this.hectares = squareMeters / 10000.0;  // 1公顷 = 10000平方米
            this.squareKilometers = squareMeters / 1000000.0;  // 1平方千米 = 1000000平方米
        }

        /**
         * 获取面积（平方米）
         */
        public double getSquareMeters() {
            return squareMeters;
        }

        /**
         * 获取面积（平方米，保留2位小数）
         */
        public BigDecimal getSquareMetersFormatted() {
            return BigDecimal.valueOf(squareMeters).setScale(2, RoundingMode.HALF_UP);
        }

        /**
         * 获取面积（亩）
         */
        public double getMu() {
            return mu;
        }

        /**
         * 获取面积（亩，保留2位小数）
         */
        public BigDecimal getMuFormatted() {
            return BigDecimal.valueOf(mu).setScale(2, RoundingMode.HALF_UP);
        }

        /**
         * 获取面积（公顷）
         */
        public double getHectares() {
            return hectares;
        }

        /**
         * 获取面积（公顷，保留4位小数）
         */
        public BigDecimal getHectaresFormatted() {
            return BigDecimal.valueOf(hectares).setScale(4, RoundingMode.HALF_UP);
        }

        /**
         * 获取面积（平方千米）
         */
        public double getSquareKilometers() {
            return squareKilometers;
        }

        /**
         * 获取面积（平方千米，保留6位小数）
         */
        public BigDecimal getSquareKilometersFormatted() {
            return BigDecimal.valueOf(squareKilometers).setScale(6, RoundingMode.HALF_UP);
        }

        @Override
        public String toString() {
            return String.format("面积: %.2f 平方米 (%.2f 亩, %.4f 公顷, %.6f 平方千米)",
                    squareMeters, mu, hectares, squareKilometers);
        }
    }
}
